#include "StreamEncoder.h"

#define ENCODE_AUDIO
#define ENCODE_VIDEO

PacketList * clonePacket(PacketList *packet)
{
	// invalid source
	if (0 == packet)
		return 0;

	// copy packet
	PacketList *ret = (PacketList*)malloc(sizeof(PacketList));
	ret->next = 0;
	ret->packet = (ogg_packet *)malloc(sizeof(ogg_packet));
	memcpy(ret->packet, packet->packet, sizeof(ogg_packet));
	ret->packet->packet = (unsigned char*)malloc(packet->packet->bytes);
	memcpy(ret->packet->packet, packet->packet->packet, packet->packet->bytes);

	// done
	return ret;
}

PacketList* createPacket(ogg_packet *packet)
{
	if (0 == packet)
		return 0;

	PacketList *ret = (PacketList*)malloc(sizeof(PacketList));
	ogg_packet *pogg_pkg = (ogg_packet*)malloc(sizeof(ogg_packet));
	memcpy(pogg_pkg, packet, sizeof(ogg_packet));
	pogg_pkg->packet = (unsigned char*)malloc(pogg_pkg->bytes);
	memcpy(pogg_pkg->packet, packet->packet, pogg_pkg->bytes);
	ret->packet = pogg_pkg;
	ret->next = 0;
	return ret;
}

void freePackets(PacketList *packets)
{
	while (packets)
	{
		free(packets->packet->packet);
		free(packets->packet);
		PacketList *cur = packets;
		packets = packets->next;
		free(cur);
	}
}

StreamEncoder::StreamEncoder(const int channels, const int sampleRate, const int quality, const int fps, const int vWidth, const int vHeight,
							 const int vquality, const int bitrate, const int keyframeshift, const int dropframe, const int fastmode) :
	m_bExit(1)
{
	m_lpAudioEncoder = new AudioEncoder(channels, sampleRate, quality);
	m_lpVideoEncoder = new VideoEncoder(fps, vWidth, vHeight, vquality, bitrate, keyframeshift, dropframe, fastmode);

	initOggStreams();
}

StreamEncoder::~StreamEncoder(void)
{
	encodeRemain();

	ogg_stream_clear(m_lpOggVideoStream);
	ogg_stream_reset(m_lpOggVideoStream);
	ogg_stream_clear(m_lpOggAudioStream);
	ogg_stream_reset(m_lpOggAudioStream);

	// exit thread
	if (!m_bExit)
	{
		m_bExit = 1;
		while (!isFinished())
			QThread::msleep(100);
	}

	// delete data
	while (m_queueVideoData.size() > 0)
	{
		VideoData *tmp = m_queueVideoData.dequeue();
		delete [] tmp->data;
		delete tmp;
	}
	while (m_queueAudioData.size() > 0)
	{
		AudioData *tmp = m_queueAudioData.dequeue();
		for (int i=0; i<m_lpAudioEncoder->audioChannels(); i++)
			delete [] tmp->data[i];
		delete [] tmp->data;
		delete tmp;
	}

	delete m_lpAudioEncoder;
	delete m_lpVideoEncoder;

	delete m_lpOggVideoStream;
	delete m_lpOggAudioStream;
}

int StreamEncoder::reset(const int channels, const int sampleRate, const int quality, const int fps, const int vWidth, const int vHeight, 
						 const int vquality, const int bitrate, const int keyframeshift, const int dropframe, const int fastmode)
{
	// delete audio data
	m_queueAudioLock.lock();

	ogg_stream_clear(m_lpOggAudioStream);
	ogg_stream_reset(m_lpOggAudioStream);
	ogg_stream_init(m_lpOggAudioStream, 110983);
	delete m_lpAudioEncoder;
	m_lpAudioEncoder = new AudioEncoder(channels, sampleRate, quality);

	while (m_queueAudioData.size() > 0)
	{
		AudioData *tmp = m_queueAudioData.dequeue();
		for (int i=0; i<m_lpAudioEncoder->audioChannels(); i++)
			free((void*)tmp->data[i]);
		free(tmp->data);
		free(tmp);
	}
	m_queueAudioLock.unlock();

	// delete video data
	m_queueVideoLock.lock();

	ogg_stream_clear(m_lpOggVideoStream);
	ogg_stream_reset(m_lpOggVideoStream);
	ogg_stream_init(m_lpOggVideoStream, 121083);
	delete m_lpVideoEncoder;
	m_lpVideoEncoder = new VideoEncoder(fps, vWidth, vHeight, vquality, bitrate, keyframeshift, dropframe, fastmode);

	while (m_queueVideoData.size() > 0)
	{
		VideoData *tmp = m_queueVideoData.dequeue();
		free(tmp->data);
		free(tmp);
	}
	m_queueVideoLock.unlock();

	return 0;
}

void StreamEncoder::initOggStreams()
{
	m_lpOggVideoStream = new ogg_stream_state;
	ogg_stream_init(m_lpOggVideoStream, 121083);

	m_lpOggAudioStream = new ogg_stream_state;
	ogg_stream_init(m_lpOggAudioStream, 110983);
}

int StreamEncoder::encodeVideoPackets(PacketList *packets, int bFlushed)
{
	ogg_page page;
	PacketList *tmp = packets;

	// put all received packets to stream
	while (tmp)
	{
		if (ogg_stream_packetin(m_lpOggVideoStream, tmp->packet) < 0)
			exit(1);
		tmp = tmp->next;
	}

	// get all pages out and fire signal
	if (!bFlushed)
	{
		while (ogg_stream_pageout(m_lpOggVideoStream, &page) > 0)
			emit onProcessOggPage(page.header, page.header_len, page.body, page.body_len);
	}
	else
	{
		while (ogg_stream_flush(m_lpOggVideoStream, &page) > 0)
			emit onProcessOggPage(page.header, page.header_len, page.body, page.body_len);
	}
	return 0;
}

int StreamEncoder::encodeAudioPackets(PacketList *packets, int bFlushed)
{
	ogg_page page;
	PacketList *tmp = packets;

	// put all received packets to stream
	while (tmp)
	{
		if (ogg_stream_packetin(m_lpOggAudioStream, tmp->packet) < 0)
			exit(1);
		tmp = tmp->next;
	}

	if (!bFlushed)
	{
		while (ogg_stream_pageout(m_lpOggAudioStream, &page) > 0)
			emit onProcessOggPage(page.header, page.header_len, page.body, page.body_len);
	}
	else
	{
		while (ogg_stream_flush(m_lpOggAudioStream, &page) > 0)
			emit onProcessOggPage(page.header, page.header_len, page.body, page.body_len);
	}
	return 0;
}

int StreamEncoder::encodeHeader()
{
	int ret = 0;

#ifdef ENCODE_VIDEO
	// encode first video header packet
	PacketList *vPackets = m_lpVideoEncoder->headerPackets();
	emit onProcessVideoHeaders(vPackets);

	PacketList *vFirstPacket = vPackets;
	vPackets = vPackets->next;
	vFirstPacket->next = 0;
	ret = encodeVideoPackets(vFirstPacket, 1);
	freePackets(vFirstPacket);
	if (ret)
		return ret;
#endif

#ifdef ENCODE_AUDIO
	// encode first audio header packet
	PacketList *aPackets = m_lpAudioEncoder->headerPackets();
	emit onProcessAudioHeaders(aPackets);

	PacketList *aFirstPacket = aPackets;
	aPackets = aPackets->next;
	aFirstPacket->next = 0;
	ret = encodeAudioPackets(aFirstPacket, 1);
	freePackets(aFirstPacket);
	if (ret)
		return ret;
#endif

#ifdef ENCODE_VIDEO
	// encode remain video header packets
	ret = encodeVideoPackets(vPackets, 1);
	freePackets(vPackets);
	if (ret)
		return ret;
#endif

#ifdef ENCODE_AUDIO
	// encode remain audio header packets
	ret = encodeAudioPackets(aPackets, 1);
	freePackets(aPackets);
#endif

	return ret;
}

int StreamEncoder::encodeRemain()
{
#ifdef ENCODE_VIDEO
	PacketList *vPackets = m_lpVideoEncoder->remainPackets();
	encodeVideoPackets(vPackets, 1);
#endif

#ifdef ENCODE_AUDIO
	PacketList *aPackets = m_lpAudioEncoder->remainPackets();
	encodeAudioPackets(aPackets, 1);
#endif

	return 0;
}

int StreamEncoder::encodeVideo(const unsigned char *data)
{
	// encode using video encoder
	PacketList * p = m_lpVideoEncoder->encodeRGB(data);
	if (p)
	{
		encodeVideoPackets(p);
		emit onProcessVideoPackets(p);
		freePackets(p);
	}
	return 0;
}

int StreamEncoder::encodeAudio(const TYPE_SAMPLE_IN **data, const int size)
{
	// encode using audio encoder
	PacketList * p = m_lpAudioEncoder->encodeAudio(data, size);
	if (p)
	{
		encodeAudioPackets(p);
		emit onProcessAudioPackets(p);
		freePackets(p);
	}
	return 0;
}

void StreamEncoder::run()
{
	m_bExit = 0;
	while (!m_bExit)
	{
#ifdef ENCODE_VIDEO
		VideoData * vdata = 0;
		m_queueVideoLock.lock();
		if (m_queueVideoData.size() > 0)
			vdata = m_queueVideoData.dequeue();
		m_queueVideoLock.unlock();

		if (vdata)
		{
			// encode using video encoder
			PacketList * p = m_lpVideoEncoder->encodeRGB(vdata->data);
			if (p)
			{
				//encodeVideoPackets(p, 1);
				encodeVideoPackets(p);
				freePackets(p);
			}
			free(vdata->data);
			free(vdata);
		}
#endif

#ifdef ENCODE_AUDIO
		AudioData * adata = 0;
		m_queueAudioLock.lock();
		if (m_queueAudioData.size() > 0)
			adata = m_queueAudioData.dequeue();
		m_queueAudioLock.unlock();

		if (adata)
		{
			// encode using audio encoder
			PacketList * p = m_lpAudioEncoder->encodeAudio((const TYPE_SAMPLE_IN**)adata->data, adata->size);
			if (p)
			{
				//encodeAudioPackets(p, 1);
				encodeAudioPackets(p);
				freePackets(p);
			}
			for (int i=0; i<m_lpAudioEncoder->audioChannels(); i++)
				free((void*)adata->data[i]);
			free(adata->data);
			free(adata);
		}
#endif
		QThread::msleep(1);
	}
}
